﻿using System;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;

namespace UnitTesting.Fixturing
{
	static class Ensure
	{
		internal static T IsNotNull<T>(Expression<Func<T>> expression) where T : class
		{
			if (expression == null) throw new ArgumentNullException("expression");
			return IsNotNull(GetValue(expression), GetParamName(expression));
		}

		static T GetValue<T>(Expression<Func<T>> expression) where T : class
		{
			return expression.Compile()();
		}

		[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "MemberExpression")]
		static string GetParamName<T>(Expression<Func<T>> expression)
		{
			var memberExpression = expression.Body as MemberExpression;
			if (memberExpression != null)
			{
				return memberExpression.Member.Name;
			}
			throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture,
			                                              "The type of the expression body should be '{1}' type but '{0}' type",
			                                              expression.Body.GetType(), "MemberExpression"));
		}

		internal static T IsNotNull<T>(T value, string paramName) where T : class
		{
			if (value == null)
			{
				throw new ArgumentNullException(paramName);
			}
			return value;
		}

		internal static T IsNotAnyNull<T>(Expression<Func<T>> expression) where T : class, IEnumerable
		{
			if (expression == null) throw new ArgumentNullException("expression");
			return IsNotAnyNull(GetValue(expression), GetParamName(expression));
		}

		internal static T IsNotAnyNull<T>(T value, string paramName) where T : class, IEnumerable
		{
			if (value.Cast<object>().Any(x => x == null))
			{
				throw new ArgumentException(
					string.Format(CultureInfo.InvariantCulture, "The parameter '{0}' cannot have null item.", paramName),
					paramName);
			}
			return value;
		}

		internal static string IsNotNullOrEmpty(Expression<Func<string>> expression)
		{
			if (expression == null) throw new ArgumentNullException("expression");
			return IsNotNullOrEmpty(GetValue(expression), GetParamName(expression));
		}

		internal static string IsNotNullOrEmpty(string value, string paramName)
		{
			if (string.IsNullOrEmpty(value))
			{
				throw new ArgumentException(
					string.Format(CultureInfo.InvariantCulture, "The '{0}' cannot be null or empty string.", paramName),
					paramName);
			}
			return value;
		}
	}
}